package edu.mercer.sse.configuration;

import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;

import static com.google.common.base.Preconditions.*;
import edu.mercer.sse.annotations.Fulfills;

/**
 * Read-only access to application properties.<p>
 * 
 * Wrapper for Properties. Limits access to Properties collection. Provide 
 * 	immutable access only.<p>
 * 
 * Does not return null. Returns empty String for any missing values.<p>
 *
 * @see AppProperties
 * 
 * @see Properties
 */

public class AppProp implements AppProperties<String,String> 
{
	/** Properties if supplied to constructor */
	
	private Optional<Properties> _prop = Optional.absent();
	
	
	/** 
	 * ConcurrentMap&#60;String,AppProperties&#60;String,String&#62;&#62; 
	 * 	serving as object cache.<p>
	 * 
	 * Singleton. Only one global instance of AppProp is created.<p>
	 */
	
	private static final ConcurrentMap<String,AppProperties<String,String>> 
		_singletonThis = new ConcurrentHashMap<>();
	

	/**
	 * Constructor.<p>
	 * 
	 * Requires non-empty Properties collection.<p>
	 * 
	 * @param propertiesCollection - Properties
	 * 
	 * @see Properties
	 * 
	 * @see #factory(Properties)
	 */
		
	private AppProp( final Properties propertiesCollection )
	{
		checkNotNull( propertiesCollection, 
				"null properties collection" );
		
		checkArgument( propertiesCollection.size() > 0, 
				"empty properties collection" );
		
		_prop = Optional.of( propertiesCollection );
		
	}//constructor
	

	/**
	 * Factory.<p>
	 * 
	 * Requires non-empty Properties collection. Returns limited interface 
	 * 	AppProperties. Restricts access to underlying Properties and prevents 
	 * 	modification.<p>
	 * 
	 * @param propertiesCollection - Properties
	 * 
	 * @return  AppProperties
	 * 
	 * @see #AppProp(Properties)
	 * 
	 * @see AppProperties
	 */
	
	public static AppProperties<String,String> factory( final Properties 
			propertiesCollection )
	{
		_singletonThis.putIfAbsent( Config.getAppName(), 
									new AppProp(propertiesCollection) );
		
		return _singletonThis.get( Config.getAppName() );
		
	} //method
		
	
	@Fulfills("AppProperties")
	public String get( final String propertyName ) 
	{
		return _prop.get().getProperty(propertyName, "").trim();
	
	}//method

	
	@Override
	public ImmutableMap<String, String> getItems() 
	{
		final Enumeration<Object> keyObjects = _prop.get().keys();
		
		final Map<String,String> propertyMap = Maps.newHashMap();
		
		while( keyObjects.hasMoreElements() )
		{
			Object keyObj = keyObjects.nextElement();
			
			String keyName = (String) keyObj;
			
			if( _prop.get().containsKey(keyName) )
			{ 
				propertyMap.put( keyName, _prop.get().getProperty(keyName) );
			
			}//if
			
		}//while
		
		checkArgument( _prop.get().size() > 0, "empty property collection" );
		
		/* immutable, with each String entry value trimmed */
		
		return ImmutableMap.copyOf( Maps.transformValues(propertyMap, 
														 new TrimFunction()) );
		
	}//method
	
	
}//class
